/*
 * Copyright (c) 2011 Eugenio Realini, Mirko Reguzzoni, Cryms sagl - Switzerland. All Rights Reserved.
 *
 * This file is part of goGPS Project (goGPS).
 *
 * goGPS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3
 * of the License, or (at your option) any later version.
 *
 * goGPS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with goGPS.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package com.galfins.gogpsextracts.Gal;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.galfins.gogpsextracts.Ephemeris;
import com.galfins.gogpsextracts.EphemerisResponse;
import com.galfins.gogpsextracts.GnssEphemeris;
import com.galfins.gogpsextracts.Iono;
import com.galfins.gogpsextracts.KeplerianModel;
import com.galfins.gogpsextracts.NavigationProducer;
import com.galfins.gogpsextracts.SatellitePosition;

import java.util.ArrayList;

import static com.galfins.gogpsextracts.GetEphFromIgmas.GetGALEphUrl;
import static com.galfins.gogpsextracts.GetEphFromIgmas.httpGet;

/**
 * @author Lorenzo Patocchi, cryms.com
 * <p>
 * This class retrieve RINEX file on-demand from known server structures
 */
public class RinexNavigationGalileo implements NavigationProducer {
    RinexNavigationParserGalileo m_rnp = null;
    long m_ephTimestamp = 0;

    public SatellitePosition getGalileoSatPosition(long unixTime, double range, int satID, char satType, double receiverClockError) {

        RinexNavigationParserGalileo rnp = getRNPByTimestamp(unixTime);

        if (rnp == null)
            return null;

        EphGalileo eph = rnp.findEph(unixTime, satID, satType);
        if (eph == null)
            return null;

        if (eph.getRootA() < 4500)
            return null;

        if (rnp != null) {
            if (rnp.isTimestampInEpocsRange(unixTime)) {
                return rnp.getGalileoSatPosition(unixTime, range, satID, satType, receiverClockError);
            } else {
                return null;
            }
        }

        return null;
    }

    public SatellitePosition getGalileoSatVelocities(long unixTime, double range, int satID, char satType, double receiverClockError) {

        RinexNavigationParserGalileo rnp = getRNPByTimestamp(unixTime);
        if (rnp != null) {
            if (rnp.isTimestampInEpocsRange(unixTime)) {

                return rnp.getGalileoSatVelocities(unixTime, range, satID, satType, receiverClockError);

            } else {
                return null;
            }
        }

        return null;
    }

    protected RinexNavigationParserGalileo getEph() {
        ArrayList<GnssEphemeris> ephList = new ArrayList<GnssEphemeris>();
        Ephemeris.IonosphericModelProto.Builder ionoBuilder = Ephemeris.IonosphericModelProto.newBuilder();
        RinexNavigationParserGalileo rnp = null;
        String str = httpGet(GetGALEphUrl);
        JSONObject object = JSON.parseObject(str);
        if (object != null) {
            m_ephTimestamp = object.getLong("timestamp");

            JSONObject iono = object.getJSONObject("iono");
            ionoBuilder.addAlpha(iono.getDouble("alpha0"));
            ionoBuilder.addAlpha(iono.getDouble("alpha1"));
            ionoBuilder.addAlpha(iono.getDouble("alpha2"));
            ionoBuilder.addAlpha(iono.getDouble("alpha3"));

            ionoBuilder.addBeta(iono.getDouble("beta0"));
            ionoBuilder.addBeta(iono.getDouble("beta1"));
            ionoBuilder.addBeta(iono.getDouble("beta2"));
            ionoBuilder.addBeta(iono.getDouble("beta3"));

            Ephemeris.IonosphericModelProto ionoProto = ionoBuilder.build();
            Ephemeris.IonosphericModelProto ionoProto2 = ionoBuilder.build();

            JSONArray ephArray = object.getJSONArray("eph");

            for (int i = 0; i < ephArray.size(); ++i) {
                GalEphemeris.Builder builder = GalEphemeris.newBuilder();

                JSONObject eph = ephArray.getJSONObject(i);

                builder.setSvid(eph.getInteger("prn"));

                builder.setAf0S(eph.getDouble("af0"));

                builder.setAf1SecPerSec(eph.getDouble("af1"));

                builder.setAf2SecPerSec2(eph.getDouble("af2"));

                builder.setIode(eph.getInteger("IODnav"));

                KeplerianModel.Builder model = KeplerianModel.newBuilder();

                model.setCrs(eph.getDouble("crs"));

                model.setDeltaN(eph.getDouble("deltaN"));

                model.setM0(eph.getDouble("m0"));

                model.setCuc(eph.getDouble("cuc"));

                model.setEccentricity(eph.getDouble("e"));

                model.setCus(eph.getDouble("cus"));

                model.setSqrtA(eph.getDouble("sqrtA"));

                double toeSec = eph.getDouble("toe");
                model.setToeS(toeSec);
                builder.setTocS(toeSec);

                model.setCic(eph.getDouble("cic"));

                model.setOmega0(eph.getDouble("omega0"));

                model.setCis(eph.getDouble("cis"));

                model.setI0(eph.getDouble("i0"));

                model.setCrc(eph.getDouble("crc"));

                model.setOmega(eph.getDouble("omega"));

                model.setOmegaDot(eph.getDouble("omegaDot"));

                model.setIDot(eph.getDouble("idot"));

                builder.setWeek(eph.getInteger("week"));

                builder.setSisaM(eph.getDouble("sisa"));

                builder.setIsINav(eph.getBoolean("iNav"));

                builder.setKeplerianModel(new KeplerianModel(model));

                ephList.add(builder.build());
            }
            EphemerisResponse ephResponse = new EphemerisResponse(ephList, ionoProto, ionoProto2);
            rnp = new RinexNavigationParserGalileo(ephResponse);
        }
        return rnp;
    }

    protected RinexNavigationParserGalileo getRNPByTimestamp(long unixTime) {
        if (m_rnp == null)
        {
            m_rnp = getEph();
        }
        return m_rnp;
    }

    @Override
    public Iono getIono(long unixTime) {
        RinexNavigationParserGalileo rnp = getRNPByTimestamp(unixTime);
        if (rnp != null) return rnp.getIono(unixTime);
        return null;
    }
}
